﻿using System;
using BusinessBingo.CommandHandlers.Base;
using BusinessBingo.CommandHandlers.Interfaces;
using BusinessBingo.CommandHandlers.Results;
using BusinessBingo.Commands;
using BusinessBingo.Data.Interfaces;
using BusinessBingo.Model;
using BusinessBingo.Model.Interfaces;
using BusinessBingo.Queries;
using BusinessBingo.QueryHandlers.Interfaces;
using BusinessBingo.QueryHandlers.Results;

namespace BusinessBingo.CommandHandlers
{
    public class JoinGroupCommandHandler : BaseCommandHandler, IJoinGroupCommandHandler
    {
        private IGetGroupQueryHandler _getGroupQueryHandler;
        private ICreateUserCommandHandler _createUserCommandHandler;
        private IGetLastGameOfGroupQueryHandler _getLastGameOfGroupQueryHandler;
        private ICreateNewGameCommandHandler _createNewGameCommandHandler;
        private IGetRandomBingoSetQueryHandler _getRandomBingoSetQueryHandler;
        private ICreateBingoSetCommandHandler _createBingoSetCommandHandler;

        private int _bingoSetSize = 4;

        public JoinGroupCommandHandler(
            IGetGroupQueryHandler getGroupQueryHandler,
            ICreateUserCommandHandler createUserCommandHandler,
            IGetLastGameOfGroupQueryHandler getLastGameOfGroupQueryHandler,
            ICreateNewGameCommandHandler createNewGameCommandHandler,
            IGetRandomBingoSetQueryHandler getRandomBingoSetQueryHandler,
            ICreateBingoSetCommandHandler createBingoSetCommandHandler,
            IValidationState validationState,
            IUnitOfWork unitOfWork) : base(validationState, unitOfWork)
        {
            this._getGroupQueryHandler = getGroupQueryHandler;
            this._createUserCommandHandler = createUserCommandHandler;
            this._getLastGameOfGroupQueryHandler = getLastGameOfGroupQueryHandler;
            this._createNewGameCommandHandler = createNewGameCommandHandler;
            this._getRandomBingoSetQueryHandler = getRandomBingoSetQueryHandler;
            this._createBingoSetCommandHandler = createBingoSetCommandHandler;
        }

        public JoinGroupCommandHandlerResult Execute(JoinGroupCommand joinGroupCommand)
        {
            JoinGroupCommandHandlerResult result = new JoinGroupCommandHandlerResult();

            if(joinGroupCommand == default(JoinGroupCommand))
                throw new ArgumentNullException("JoinGroupCommand");
            if(joinGroupCommand.GroupId == default(Guid))
                this._validationState.AddError<JoinGroupCommand>(x => x.GroupId, ErrorTypes.Empty);
            if(string.IsNullOrWhiteSpace(joinGroupCommand.UserName) == true)
                this._validationState.AddError<JoinGroupCommand>(x => x.UserName, ErrorTypes.Empty);

            if (this._validationState.IsValid == false)
                return result;

            GetGroupQuery getGroupQuery = new GetGroupQuery(joinGroupCommand.GroupId);
            GetGroupQueryHandlerResult getGroupQueryQueryResult = this._getGroupQueryHandler.Execute(getGroupQuery);

            CreateUserCommand createUserCommand = new CreateUserCommand(joinGroupCommand.UserName, joinGroupCommand.GroupId);
            CreateUserCommandHandlerResult createUserCommandHandlerResult =
                this._createUserCommandHandler.Execute(createUserCommand);

            GetLastGameOfGroupQuery getLastGameOfGroupQuery = new GetLastGameOfGroupQuery(joinGroupCommand.GroupId);
            GetLastGameOfGroupQueryHandlerResult getLastGameOfGroupQueryHandlerResult =
                this._getLastGameOfGroupQueryHandler.Execute(getLastGameOfGroupQuery);

            Guid gameId;
            int gameNumber;
            if(getLastGameOfGroupQueryHandlerResult.HasGames == true)
            {
                gameId = getLastGameOfGroupQueryHandlerResult.LastGame.Id;
                gameNumber = getLastGameOfGroupQueryHandlerResult.LastGame.Number;
            }
            else
            {
                CreateNewGameCommand createNewGameCommand = new CreateNewGameCommand(joinGroupCommand.GroupId);
                CreateNewGameCommandHandlerResult createNewGameCommandHandlerResult =
                    this._createNewGameCommandHandler.Execute(createNewGameCommand);
                gameId = createNewGameCommandHandlerResult.Game.Id;
                gameNumber = createNewGameCommandHandlerResult.Game.Number;
            }

            GetRandomBingoSetQuery getRandomBingoSetQuery = new GetRandomBingoSetQuery(this._bingoSetSize,
                                                                                       getGroupQueryQueryResult.Group.
                                                                                           Language);
            GetRandomBingoSetQueryHandlerResult getRandomBingoSetQueryHandlerResult =
                this._getRandomBingoSetQueryHandler.Execute(getRandomBingoSetQuery);

            CreateBingoSetCommand createBingoSetCommand =
                new CreateBingoSetCommand(getRandomBingoSetQueryHandlerResult.BingoSet, gameId,
                                          createUserCommandHandlerResult.User.Id);

            CreateBingoSetCommandHandlerResult createBingoSetCommandHandlerResult =
                this._createBingoSetCommandHandler.Execute(createBingoSetCommand);

            base.Complete();

            result.Succeeded = true;
            result.UserId = createUserCommandHandlerResult.User.Id;
            result.GameId = gameId;
            result.GameNumber = gameNumber;

            return result;
        }
    }
}